float4x4 world;
float4x4 view;
float4x4 projection;
float4x4 worldInverseTranspose;
float4x4 worldViewProj;

//Vertex colors
float3 DiffuseColor;
float3 EmissiveColor;
float3 SpecularColor;
float SpecularPower;
float Alpha;

struct Light
{
	float3 dir;                     // world space direction
	float3 pos;                     // world space position
	float4 ambient;
	float4 diffuse;
	float4 specular;
	float spotInnerCone;            // spot light inner cone (theta) angle
	float spotOuterCone;            // spot light outer cone (phi) angle
	float radius;                   // applies to point and spot lights only
};

Light light[3];

float3 cameraPos;
float4 globalAmbient;

texture shadowMapTexture;
texture screenMapTexture;

float4x4 lightView;
float4x4 lightProj;
float4x4 lightViewProj;
float4x4 textureMatrix;

sampler shadowMapSampler = sampler_state
{
	Texture = <shadowMapTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Clamp;
	AddressV = Clamp;
};

sampler screenMapSampler = sampler_state
{
	Texture = <screenMapTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Clamp;
	AddressV = Clamp;
};

struct VSInput
{
	float4 position : POSITION0;
	float3 normal : NORMAL0;
	float2 texCoord : TEXCOORD0;
};

struct VSOutput_Dir
{
	float4 position : POSITION0;	
	float3 halfVector: TEXCOORD1;
	float3 lightDir : TEXCOORD2;
	float3 normal : TEXCOORD3;
	float4 diffuse : COLOR0;
	float4 specular : COLOR1;
};

VSOutput_Dir VS_DirectionalLighting(VSInput IN)
{
	VSOutput_Dir OUT;

	float4x4 worldViewProjection = mul(world, mul(view, projection));

	float3 worldPos = mul(IN.position, world).xyz;
	float3 viewDir = cameraPos - worldPos;
		
	OUT.position = mul(IN.position, worldViewProjection);
	OUT.lightDir = -light[0].dir;
	OUT.halfVector = normalize(normalize(OUT.lightDir) + normalize(viewDir));
	OUT.normal = mul(IN.normal, (float3x3)worldInverseTranspose);
	OUT.diffuse = float4(DiffuseColor, Alpha)  * light[0].diffuse;
	OUT.specular = float4(SpecularColor, Alpha) * light[0].specular;

	return OUT;
}

float4 PS_DirectionalLighting(VSOutput_Dir IN) : COLOR0
{
	float3 n = normalize(IN.normal);
	float3 h = normalize(IN.halfVector);
	float3 l = normalize(IN.lightDir);
	
	float nDotL = saturate(dot(n, l));
	float nDotH = saturate(dot(n, h));
	float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, SpecularPower);	  

	float4 color = (float4(DiffuseColor, Alpha) * (globalAmbient + light[0].ambient)) +
				   (IN.diffuse * nDotL) + (IN.specular * power);


	return color ;//* tex2D(colorMap, IN.texCoord);
}

struct VSOutput_Shadow
{
	float4 position : POSITION0;
	float depth : TEXCOORD0;
};

VSOutput_Shadow VS_RenderShadowMap(float4 vPos: POSITION)
{
	VSOutput_Shadow Out;
	Out.position = mul(vPos, lightViewProj);
	Out.depth = Out.position.z;	
	return Out;
}

float4 PS_RenderShadowMap( VSOutput_Shadow In ) : COLOR
{ 
	return float4(In.depth, In.depth , In.depth ,1.0f);
}

struct VSOutput_Unlit
{
	float4 position : POSITION0;
	float4 texCoord : TEXCOORD0;
	float depth : TEXCOORD1;
};

VSOutput_Unlit VS_Unlit( float4 inPosition : POSITION )
{
   // Output struct
   VSOutput_Unlit OUT = (VSOutput_Unlit)0;
 
   // Output the transformed position
   OUT.position = mul( inPosition, worldViewProj );
 
   // Output the projective texture coordinates
   OUT.texCoord = mul( inPosition, textureMatrix );
 
   // Output the scene depth
   OUT.depth = mul( inPosition, lightViewProj ).z;
 
   return OUT;
}

float4  PS_Unlit( VSOutput_Unlit IN ) : COLOR0
{
   // Generate the 9 texture co-ordinates for a 3x3 PCF kernel
   float4 texCoords[9];
   // Texel size
   float fTexelSize = 1.0f / 1024.0f;
 
   // Generate the tecture co-ordinates for the specified depth-map size
   // 4 3 5
   // 1 0 2
   // 7 6 8
   texCoords[0] = IN.texCoord;
   texCoords[1] = IN.texCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );
   texCoords[2] = IN.texCoord + float4(  fTexelSize, 0.0f, 0.0f, 0.0f );
   texCoords[3] = IN.texCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
   texCoords[6] = IN.texCoord + float4( 0.0f,  fTexelSize, 0.0f, 0.0f );
   texCoords[4] = IN.texCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
   texCoords[5] = IN.texCoord + float4(  fTexelSize, -fTexelSize, 0.0f, 0.0f );
   texCoords[7] = IN.texCoord + float4( -fTexelSize,  fTexelSize, 0.0f, 0.0f );
   texCoords[8] = IN.texCoord + float4(  fTexelSize,  fTexelSize, 0.0f, 0.0f );
   
   // Sample each of them checking whether the pixel under test is shadowed or not
   float fShadowTerms[9];
   float fShadowTerm = 0.0f;
   for( int i = 0; i < 9; i++ )
   {
	  float A = tex2Dproj( shadowMapSampler, texCoords[i] ).r;
	  float B = IN.depth - 0.03f;
	  // Texel is shadowed
	  fShadowTerms[i] = A < B ? 0.3f : 1.0f;
	  fShadowTerm	 += fShadowTerms[i];
   }
   // Get the average
   fShadowTerm /= 9.0f;
   return fShadowTerm;
}

struct VSOUTPUT_SCENE
{
   float4 vPosition	  : POSITION;
   float2 vTexCoord	  : TEXCOORD0;
   float4 vScreenCoord   : TEXCOORD1;
   float3 vNormal		: TEXCOORD2;
   float4 vWorldPos	  : TEXCOORD3;
};
// Scene vertex shader
VSOUTPUT_SCENE VS_Scene( float4 inPosition : POSITION, float3 inNormal : NORMAL,
						 float2 inTexCoord : TEXCOORD0 )
{
   VSOUTPUT_SCENE OUT = (VSOUTPUT_SCENE)0;

   // Output the transformed position
   OUT.vPosition = mul( inPosition, worldViewProj );

   // Output the texture coordinates
   OUT.vTexCoord = inTexCoord;

   // Output the screen-space texture coordinates
   OUT.vScreenCoord.x = ( OUT.vPosition.x * 0.5 + OUT.vPosition.w * 0.5 );
   OUT.vScreenCoord.y = ( OUT.vPosition.w * 0.5 - OUT.vPosition.y * 0.5 );
   OUT.vScreenCoord.z = OUT.vPosition.w;
   OUT.vScreenCoord.w = OUT.vPosition.w;

   // Get the world space vertex position
   OUT.vWorldPos = mul( inPosition, world );

   // Output the world space normal
   OUT.vNormal = mul( inNormal, worldInverseTranspose );   

   return OUT;
}

float4 PS_Scene( VSOUTPUT_SCENE IN ) : COLOR0
{
   IN.vNormal = normalize( IN.vNormal );   
   float3 vEyeVec = normalize( cameraPos.xyz - IN.vWorldPos.xyz );
   float ambient = 0.0f;
   float diffuse = 0.0f;
   float specular = 0.0f;

   for(int i = 0; i < 3; i++)
   {
		float3 vLightVec = normalize( light[i].pos.xyz - IN.vWorldPos.xyz );   
		
		diffuse  += max( dot( IN.vNormal, vLightVec ), 0 );
		specular += pow(max(dot( 2 * dot( IN.vNormal, vLightVec ) * IN.vNormal
										- vLightVec, vEyeVec ), 0 ), 8 );
		if( diffuse == 0 ) specular = 0;
   }
   
   // Grab the shadow term
   float fShadowTerm = tex2Dproj( screenMapSampler, IN.vScreenCoord );

   // Compute the final color
   return (ambient * float4(DiffuseColor, Alpha)) +
		  (diffuse * float4(DiffuseColor, Alpha) * light[0].diffuse * fShadowTerm ) +
		  (specular * float4(SpecularColor, Alpha) * light[0].specular * fShadowTerm );
}

technique Scene
{
	pass P0
	{
		VertexShader = compile vs_3_0 VS_Scene();
		PixelShader  = compile ps_3_0 PS_Scene();	
	}
}

technique Unlit
{
	pass P0
	{
		VertexShader = compile vs_3_0 VS_Unlit();
		PixelShader  = compile ps_3_0 PS_Unlit();	
	}
}

technique PerPixelDirectionalLighting
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 VS_DirectionalLighting();
		PixelShader = compile ps_3_0 PS_DirectionalLighting();
	}
}

technique ShadowMapRender
{
	pass P0
	{
		VertexShader = compile vs_3_0 VS_RenderShadowMap();
		PixelShader  = compile ps_3_0 PS_RenderShadowMap();	
	}
}

